<script setup lang="ts">
import { ref, h } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { ControlButton, Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'
import Dialog from './Dialog.vue'
import useDialog from './ts/useDialog';
import Icon from './icons/Icon.vue'
import HttpNode from './flowComponents/HttpNode.vue';
import AgentNode from './flowComponents/AgentNode.vue';
import AgentIfNode from './flowComponents/AgentIfNode.vue';
import ToolExecuteNode from './flowComponents/ToolExecuteNode.vue'
import ToolNode from './flowComponents/ToolNode.vue';
import HttpInNode from './flowComponents/HttpInNode.vue';
import HttpOutNode from './flowComponents/HttpOutNode.vue';
import HttpAudioInNode from './flowComponents/HttpAudioInNode.vue';
import STTNode from './flowComponents/STTNode.vue';
import { onMounted, onUnmounted } from 'vue'
import {useProjectsStore} from '@/stores/projectsStore';
const { updateProjectContent, projects } = useProjectsStore();

const props = defineProps<{
  id: string;
  style?: Record<string, any>; // 添加 style 属性
}>()

/**
 * `useVueFlow` provides:
 * 1. a set of methods to interact with the VueFlow instance (like `fitView`, `setViewport`, `addEdges`, etc)
 * 2. a set of event-hooks to listen to VueFlow events (like `onInit`, `onNodeDragStop`, `onConnect`, etc)
 * 3. the internal state of the VueFlow instance (like `nodes`, `edges`, `viewport`, etc)
 */
const { onInit, onNodeDragStop, addEdges, setViewport, toObject, 
  onNodesChange, onEdgesChange, applyNodeChanges, applyEdgeChanges, onNodeDrag,
  findNode,addNodes, updateNode
} = useVueFlow()

const dialog = useDialog()

const nodes = ref(projects.find(p => p.id === props.id)?.content.initialNodes)

const edges = ref(projects.find(p => p.id === props.id)?.content.initialEdges)

// our dark mode toggle flag
const dark = ref(false)

/**
 * This is a Vue Flow event-hook which can be listened to from anywhere you call the composable, instead of only on the main component
 * Any event that is available as `@event-name` on the VueFlow component is also available as `onEventName` on the composable and vice versa
 *
 * onInit is called when the VueFlow viewport is initialized
 */
onInit((vueFlowInstance) => {
  // instance is the same as the return of `useVueFlow`
  vueFlowInstance.fitView()
})
onNodeDrag(({event, nodes, node})=>{
  if(node.extent!=null && node.extent.find('parent')!=undefined){

    console.log('drag Prevent')
    event.preventDefault()
  }
})
/**
 * onNodeDragStop is called when a node is done being dragged
 *
 * Node drag events provide you with:
 * 1. the event object
 * 2. the nodes array (if multiple nodes are dragged)
 * 3. the node that initiated the drag
 * 4. any intersections with other nodes
 */
onNodeDragStop(({ event, nodes, node }) => {
  updateNode(node.id, {...node})
  console.log('Node Drag Stop', { event, nodes, node })
})

/**
 * To update a node or multiple nodes, you can
 * 1. Mutate the node objects *if* you're using `v-model`
 * 2. Use the `updateNode` method (from `useVueFlow`) to update the node(s)
 * 3. Create a new array of nodes and pass it to the `nodes` ref
 */
function updatePos() {
  nodes.value = nodes.value.map((node) => {
    return {
      ...node,
      position: {
        x: Math.random() * 400,
        y: Math.random() * 400,
      },
    }
  })
}

/**
 * toObject transforms your current graph data to an easily persist-able object
 */
function logToObject() {
  console.log(toObject())
}

/**
 * Resets the current viewport transformation (zoom & pan)
 */
function resetTransform() {
  setViewport({ x: 0, y: 0, zoom: 1 })
}

function toggleDarkMode() {
  dark.value = !dark.value
}


// confirm check dialog
import {dialogMsg} from './ts/useDialog'
onNodesChange(async (changes) => {
  const nextChanges = []
  
  for (const change of changes) {
    if (change.type === 'remove') {
      const isConfirmed = await dialog.confirm(dialogMsg(change.id))
      if (isConfirmed) {
        nextChanges.push(change)
      }
    } else {
      nextChanges.push(change)
    }
  }
  applyNodeChanges(nextChanges)
  updateProjectContent(props.id,{initialEdges:toObject().edges, initialNodes:toObject().nodes })
})
onEdgesChange(async (changes) => {
  const nextChanges = []

  for (const change of changes) {
    if (change.type === 'remove') {
      const isConfirmed = await dialog.confirm(dialogMsg(change.id))

      if (isConfirmed) {
        nextChanges.push(change)
      }
    } else {
      nextChanges.push(change)
    }
  }

  applyEdgeChanges(nextChanges)
  updateProjectContent(props.id, {initialEdges:toObject().edges, initialNodes:toObject().nodes })
})


import {useDDrag} from './ts/useDDrag';
const { panOnDrag, onDKeyDown, onDKeyUp } = useDDrag()
onMounted(() => {
  window.addEventListener('keydown', onDKeyDown);
  window.addEventListener('keyup', onDKeyUp);
});
onUnmounted(() => {
  window.removeEventListener('keydown', onDKeyDown);
  window.removeEventListener('keyup', onDKeyUp);
});
interface ConnectStartParams {
  nodeId: string;
  handleId: string;
  handleType: 'source' | 'target';
}
// Connection Valid
import useConnectionValid from './flowComponents/ts/connectionValid';
const {setDraggingLineSourceHandleId, unsetDraggingLineSourceHandleId} = useConnectionValid()
function onConnectStart({nodeId, handleId, handleType}: ConnectStartParams) {
  setDraggingLineSourceHandleId(nodeId, handleId, handleType)
}
function onConnectEnd() {
  unsetDraggingLineSourceHandleId()
}
function onConnect(params:any) {
   addEdges(params)
}

// Copy Selected
import useCopySelected from './ts/useCopySelected';
const {copySelected, getPasteNodesAndEdges} = useCopySelected()
const {getSelectedEdges, getSelectedNodes} = useVueFlow()
const handleKeyDown = (event:any) => {
  if (event.ctrlKey && event.key === 'c') {
    copySelected(getSelectedNodes.value, getSelectedEdges.value);
  }
  if (event.ctrlKey && event.key === 'v') {
    const {nodes,edges} = getPasteNodesAndEdges()
    addNodes(nodes)
    addEdges(edges)
  }
};
onMounted(() => {
  document.addEventListener('keydown', handleKeyDown);
});


onUnmounted(() => {
  document.removeEventListener('keydown', handleKeyDown);
});


</script>

<template>
  <VueFlow 
    :nodes="nodes"
    :edges="edges"
    :class="{ dark }"
    class="basic-flow" 
    :default-viewport="{ zoom: 0.5 }"
    :min-zoom="0.2"
    :max-zoom="4"
    :pan-on-drag="true"
    :apply-default="false" 
    @connect="onConnect"
    @connect-start="onConnectStart"
    @connect-end="onConnectEnd"
  >
    <Background pattern-color="#aaa" :gap="16" />


    <template #node-http="props">
      <HttpNode :id="props.id" :data="props.data" />
    </template>
    <template #node-agent="props">
      <AgentNode :id="props.id" :data="props.data" />
    </template>
    <template #node-agent-if="props">
      <AgentIfNode :id="props.id" :data="props.data" />
    </template>
    <template #node-tool-execute="props">
      <ToolExecuteNode :id="props.id" :data="props.data" />
    </template>
    <template #node-tool="props">
      <ToolNode :id="props.id" :data="props.data" />
    </template>
    <template #node-http-out="props">
      <HttpOutNode :id="props.id" :data="props.data" />
    </template>
    <template #node-http-in="props">
      <HttpInNode :id="props.id" :data="props.data" />
    </template>
    <template #node-http-audio-in="props">
      <HttpAudioInNode :id="props.id" :data="props.data" /> 
    </template>
    <template #node-stt="props">
      <STTNode :id="props.id" :data="props.data" />
    </template>

    <MiniMap />

    <Controls position="top-left">
      <ControlButton title="Reset Transform" @click="resetTransform">
        <Icon name="reset" />
      </ControlButton>

      <ControlButton title="Toggle Dark Mode" @click="toggleDarkMode">
        <Icon v-if="dark" name="sun" />
        <Icon v-else name="moon" />
      </ControlButton>

      <ControlButton title="Log `toObject`" @click="logToObject">
        <Icon name="log" />
      </ControlButton>
    </Controls>
    <Dialog />
  </VueFlow>
  
</template>


<style >

@import 'https://gcore.jsdelivr.net/npm/@vue-flow/core@1.42.1/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/core@1.42.1/dist/theme-default.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/controls@latest/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/minimap@latest/dist/style.css';
@import 'https://gcore.jsdelivr.net/npm/@vue-flow/node-resizer@latest/dist/style.css';


.vue-flow__minimap {
  transform: scale(75%);
  transform-origin: bottom right;
}

.basic-flow.dark {
    background:#2d3748;
    color:#fffffb
}

.basic-flow.dark .vue-flow__node {
    background:#4a5568;
    color:#fffffb
}

.basic-flow.dark .vue-flow__node.selected {
    background:#333;
    box-shadow:0 0 0 2px #2563eb
}

.basic-flow .vue-flow__controls {
    display:flex;
    flex-wrap:wrap;
    justify-content:center
}

.basic-flow.dark .vue-flow__controls {
    border:1px solid #FFFFFB
}

.basic-flow .vue-flow__controls .vue-flow__controls-button {
    border:none;
    border-right:1px solid #eee
}

.basic-flow .vue-flow__controls .vue-flow__controls-button svg {
    height:100%;
    width:100%
}

.basic-flow.dark .vue-flow__controls .vue-flow__controls-button {
    background:#333;
    fill:#fffffb;
    border:none
}

.basic-flow.dark .vue-flow__controls .vue-flow__controls-button:hover {
    background:#4d4d4d
}

.basic-flow.dark .vue-flow__edge-textbg {
    fill:#292524
}

.basic-flow.dark .vue-flow__edge-text {
    fill:#fffffb
}

.vue-flow__handle {
    height:24px;
    width:10px;
    background:#aaa;
    border-radius:4px
}

.vue-flow__edges path {
    stroke-width:3
}

.vue-flow__node textarea{
  width: 100%;
}

.vue-flow__node {
    background-color:#f3f4f6;
    padding: 5px;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-http.selected {
    box-shadow:0 0 0 2px #7aff38
}
.vue-flow__node-http input:focus {
    outline:none;
    box-shadow:0 0 0 2px #81ff47;
    transition:box-shadow .2s
}

.vue-flow__node-tool.selected {
    box-shadow:0 0 0 2px #3877ff
}
.vue-flow__node-tool input:focus {
    outline:none;
    box-shadow:0 0 0 2px #3877ff;
    transition:box-shadow .2s
}
.vue-flow__node-tool-execute {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-tool-execute.selected {
    box-shadow:0 0 0 2px #3877ff
}
.vue-flow__node-tool-execute input:focus {
    outline:none;
    box-shadow:0 0 0 2px #1f418d;
    transition:box-shadow .2s
}

.vue-flow__node-agent {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-agent.selected {
    box-shadow:0 0 0 2px #48a3f2
}
.vue-flow__node-agent input:focus {
    outline:none;
    box-shadow:0 0 0 2px #48a3f2;
    transition:box-shadow .2s
}
.vue-flow__node-agent-if {
    display: block;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}
.vue-flow__node-agent-if.selected {
    box-shadow:0 0 0 2px #48a3f2
}
.vue-flow__node-agent-if input:focus {
    outline:none;
    box-shadow:0 0 0 2px #48a3f2;
    transition:box-shadow .2s
}

.vue-flow__node-value {
    display:flex;
    align-items:center;
    gap:8px;
    padding:8px 16px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-value.selected {
    box-shadow:0 0 0 2px #ec4899
}

.vue-flow__node-value input {
    flex:1;
    padding:8px;
    border:none;
    border-radius:8px;
    box-shadow:0 0 10px #0000001a
}

.vue-flow__node-value input:focus {
    outline:none;
    box-shadow:0 0 0 2px #ec4899;
    transition:box-shadow .2s
}

.vue-flow__node-value .vue-flow__handle {
    background-color:#ec4899
}

.vue-flow__node-operator {
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:8px;
    padding:16px 24px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-operator.selected {
    box-shadow:0 0 0 2px #2563eb
}

.vue-flow__node-operator .buttons {
    display:flex;
    gap:8px
}

.vue-flow__node-operator button {
    border:none;
    cursor:pointer;
    background-color:#4a5568;
    border-radius:8px;
    color:#fff;
    box-shadow:0 0 10px #0000004d;
    width:40px;
    height:40px;
    font-size:16px;
    display:flex;
    align-items:center;
    justify-content:center
}

.vue-flow__node-operator button svg {
    width:100%;
    height:100%
}

.vue-flow__node-operator button:hover {
    background-color:#2563eb;
    transition:background-color .2s
}

.vue-flow__node-operator button.selected {
    background-color:#2563eb
}
.vue-flow__node-operator .vue-flow__handle[data-handleid=target-a] {
    top:25%
}

.vue-flow__node-operator .vue-flow__handle[data-handleid=target-b] {
    top:75%
}

.vue-flow__node-operator .vue-flow__handle {
    background-color:#2563eb
}

.vue-flow__node-result {
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:8px;
    padding:16px 24px;
    border-radius:8px;
    box-shadow:0 0 10px #0003
}

.vue-flow__node-result.selected {
    box-shadow:0 0 0 2px #5ec697
}

.vue-flow__node-result .result {
    display:flex;
    gap:8px;
    font-size:24px
}

input::-webkit-outer-spin-button,input::-webkit-inner-spin-button {
    -webkit-appearance:none;
    margin:0
}

input[type=number] {
    -moz-appearance:textfield
}
/* NodeData容器样式 */
.NodeData {
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding: 10px;
  margin-left: 40px;
  margin-right: 40px;
}

/* 输入框行样式 */
.input-row,
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 文本输入框样式 */
.text-input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 100%;
  box-sizing: border-box;
}

/* 复选框样式 */
.checkbox {
  margin-top: 5px;
}
.top-0{
  top: 10%;
}
.top-1{
  top: 25%;
}
.top-2{
  top: 40%;
}
.top-3{
  top: 55%;
}
.top-4{
  top: 70%;
}
.top-5{
  top: 85%;
}
.HandleLeftSpan {
  margin-left: 10px;
  white-space: nowrap; /* 防止内容换行 */
  overflow: hidden;    /* 隐藏溢出的内容 */
  text-overflow: ellipsis; /* 显示省略号 */
  max-width: 50px;    /* 设置最大宽度 */
  display: inline-block;
}

.HandleRightSpan {
  text-align: right;
  display: inline-block;
  white-space: nowrap; /* 防止内容换行 */
  overflow: hidden;    /* 隐藏溢出的内容 */
  text-overflow: ellipsis; /* 显示省略号 */
  max-width: 50px;    /* 设置最大宽度 */
  position: relative;
  right: 50px;
}


.NodeName {
  position: relative;
  display: inline-block;
  font-size: 14px;
  width: 100%;
  padding-left: 70px;
  padding-right: 70px;
  text-align: center;
}

.NodeName input {
  max-height: 20px;
  height: 100%;
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  color: #333;
  font-size: 16px;
  padding: 8px 12px;
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
  text-align: center;
  background-color: #00000000;
}

/* 输入框聚焦时 */
.NodeName input:focus {
  background-color: #f5f5f5;
  box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.5);
  color: #000;
}

/* 输入框没有内容时（placeholder显示） */
.NodeName input:placeholder-shown {
  color: #ffffff;
}

/* 输入框内容区域边距 */
.NodeName input::placeholder {
  color: #ccc;
  font-style: italic;
}

/* 输入框平滑过渡效果 */
.NodeName input {
  transition: background-color 0.3s, box-shadow 0.3s, color 0.3s;
}

.NodeContent{
  height: 100%;
  width: 100%;
}

.input-row,
.select-row,
.checkbox-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 0;
}

.text-input,
.select-input {
  width: 120px;
  padding: 4px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.checkbox {
  margin-left: 8px;
}
.AgentFlowInputHandle{
  background: #2563eb;
}
.AgentFlowOutputHandle{
  background: #2563eb;
}
.AgentMsg{
  background: rgb(255, 110, 70);
}
.AgentMsgs{
  background: rgb(167, 84, 0);
}
.AgentAudio{
  background: rgb(194, 133, 0);
}
.AgentTools{
  background: rgb(147, 161, 0);
}
.AgentTool{
  background: rgb(196, 214, 0);
}
.AgentToolCalls{
  background: rgb(0, 156, 73);
}
.AgentToolCall{
  background: rgb(0, 255, 119);
}
</style>